/** 一天的毫秒数 */
export const DayTmp = 86400000;
/** 检查传入的参数是否为有效的日期对象
 * @param date 任意类型，期望是日期对象
 * @returns 返回值为布尔类型，如果传入的是有效的日期对象则为true，否则为false
 * @example isValidDate(new Date()) // true
 */
export function isValidDate(date: any): date is Date {
  return date instanceof Date && !isNaN(date.getTime());
};
/** 格式化日期对象
 * @param date 日期对象
 * @param fmt 格式字符串，默认为'yyyy-MM-dd hh:mm:ss'
 * @returns 格式化后的字符串
 * @example format(new Date()) // '2023-04-01 12:00:00'
 */
export function format(date: Date, fmt: string = 'yyyy-MM-dd hh:mm:ss') {
  if (!isValidDate(date)) {
    throw new Error('Invalid date');
  }

  // 定义日期格式化映射
  const o = {
    'M+': date.getMonth() + 1,
    'd+': date.getDate(),
    'h+': date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, // 小时
    'H+': date.getHours(), // 小时
    'm+': date.getMinutes(),
    's+': date.getSeconds(),
    'q+': Math.floor((date.getMonth() + 3) / 3),
    'S': date.getMilliseconds(),
    'a': date.getHours() < 12 ? '上午' : '下午', // 上午/下午
    'A': date.getHours() < 12 ? 'AM' : 'PM', // AM/PM
  };

  // 生成正则表达式占位符
  const regexPlaceholders = Object.keys(o).map(key => `(\\${key})`).join('|');
  const regex = new RegExp(regexPlaceholders);

  // 定义匹配替换函数
  const replaceFn = (match: string) => {
    if (match.length > 1) {
      // @ts-ignore
      return ('00' + o[match.slice(1)]).slice(-match.length);
    }
    // @ts-ignore
    return o[match];
  };

  // 特殊处理年份匹配
  fmt = fmt.replace(/(y+)/g, (match) => String(date.getFullYear()).substr(4 - match.length));
  // 替换所有格式占位符
  return fmt.replace(regex, replaceFn);
}
/** 获取指定日期所在月份的第一天
 * @param date 日期对象
 * @returns 返回月份第一天的毫秒数
 * @example getFirstDayOfMonth(new Date()) // 2023-04-01 00:00:00
 */
export function getFirstDayOfMonth(date: Date) {
  if (!isValidDate(date)) {
    throw new Error('Invalid date');
  }
  return new Date(date.getFullYear(), date.getMonth(), 1)
}
/** 获取指定日期所在月份的最后一天
 * @param date 日期对象
 * @returns 返回月份最后一天的毫秒数
 * @example getLastDayOfMonth(new Date()) // 2023-04-30 23:59:59
 */
export function getLastDayOfMonth(date: Date) {
  if (!isValidDate(date)) {
    throw new Error('Invalid date');
  }
  return new Date(date.getFullYear(), date.getMonth() + 1, 0, 23, 59, 59)
}
/** 获取指定日期当天的最后时刻（23:59:59）
 * @param date 日期对象
 * @returns 返回当天最后时刻的毫秒数
 * @example getEndTimeOfDay(new Date()) // 2023-04-01 23:59:59
 */
export function getEndTimeOfDay(date: Date) {
  if (!isValidDate(date)) {
    throw new Error('Invalid date');
  }
  return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59)
}
/** 获取指定日期当天的开始时刻（00:00:00）
 * @param date 日期对象
 * @returns 返回当天开始时刻的毫秒数
 * @example getStartTimeOfDay(new Date()) // 2023-04-01 00:00:00
 */
export function getStartTimeOfDay(date: Date) {
  if (!isValidDate(date)) {
    throw new Error('Invalid date');
  }
  return new Date(date.getFullYear(), date.getMonth(), date.getDate())
}
/** 获取指定日期所在周的范围
 * @param date 日期对象
 * @returns 返回一个包含开始时间和结束时间的数组
 * @example getWeekRange(new Date()) // [2023-03-27 00:00:00, 2023-04-03 23:59:59]
 */
export function getWeekRange(date: Date) {
  if (!isValidDate(date)) {
    throw new Error('Invalid date');
  }
  const dayOfWeek = date.getDay();
  const startOfWeek = new Date(date.getTime() - dayOfWeek * 24 * 60 * 60 * 1000);
  const endOfWeek = new Date(startOfWeek.getTime() + 6 * 24 * 60 * 60 * 1000);
  return [startOfWeek, endOfWeek];
}
/** 获取本月天数
 * @param date 日期对象
 * @returns 返回本月天数
 * @example getMonthDays(new Date()) // 30
 */
export function getMonthDays(date: Date) {
  if (!isValidDate(date)) {
    throw new Error('Invalid date');
  }
  return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
}
/** 以传入时间为参考点，获取时间跨度
 * @param range 跨度值
 * @param current 时间，默认为当前时间
 * @param fmt 时间模板，参考date.format；支持timestamp
 * @example
 * getTimeRangeByDay(31) // [Date.now(), Date.now() + 31 * 86400000]
 * getTimeRangeByDay(-31) // [Date.now(), Date.now() - 31 * 86400000]
 * getTimeRangeByDay(5, new Date(2024,05,01), 'yyyy-MM-dd hh:mm:ss') // ['2024-06-01 00:00:00', '2024-06-06 23:59:59']
 */
export const getTimeRangeByDay = (range: number = -31, current: Date = new Date(), fmt: string = 'timestamp') => {
  const calcDate = new Date(current.getTime() + DayTmp * range)

  const min = range < 0 ? calcDate : current
  const max = range < 0 ? current : calcDate

  const formatTime = (date: Date) => fmt === 'timestamp' ? date.getTime() : format(date, fmt)
  return [formatTime(getStartTimeOfDay(min)), formatTime(getEndTimeOfDay(max))]
}
